const {Board, Role, utils} = require('board-game');
const extend = require('extend');

const {game: gameConfig} = require('./config');


const home = [
    [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1]
];
const bron = [
    {x: +0, y: +0 },
    {x: +1, y: +0 },
    {x: +1, y: +1 },
    {x: +0, y: +1 },
    {x: -1, y: +1 },
    {x: -1, y: +0 },
    {x: -1, y: -1 },
    {x: +0, y: -1 },
    {x: +1, y: -1 },
    {x: +2, y: -1 },
    {x: +2, y: +0 },
    {x: +2, y: +1 },
    {x: +2, y: +2 },
    {x: +1, y: +2 },
    {x: +0, y: +2 },
    {x: -1, y: +2 },
    {x: -2, y: +2 },
    {x: -2, y: +1 },
    {x: -2, y: +0 },
    {x: -2, y: -1 },
    {x: -2, y: -2 },
    {x: -1, y: -2 },
    {x: +0, y: -2 },
    {x: +1, y: -2 },
    {x: +2, y: -2 },
];
const directions = [
    {x: -1, y: 0},
    {x: +1, y: 0},
    {x: 0, y: -1},
    {x: 0, y: +1},
];
const exits = [
    {x1: +0, y1: +1, x2: +1, y2: +1},
    {x1: +1, y1: +0, x2: +1, y2: +1},
    {x1: +0, y1: -2, x2: +1, y2: -2},
    {x1: +1, y1: -1, x2: +1, y2: -2},
    {x1: -1, y1: +1, x2: -2, y2: +1},
    {x1: -2, y1: +0, x2: -2, y2: +1},
    {x1: -1, y1: -2, x2: -2, y2: -2},
    {x1: -2, y1: -1, x2: -2, y2: -2},
];

class MazeBoard extends Board {
    constructor(game) {
        super(game);
        this.tick = 0;
        this.status = gameConfig.status.waiting;
        this.rescuers = [];
        this.createMap(5 + Math.floor(Math.sqrt(this.game.groups.length * 4)));
        this.putRoles();
    }

    putRoles() {
        const center = Math.floor(this.map.length / 2);
        const homeHalfSize = Math.floor(home.length / 2);
        let index = 0;

        for (const group of this.game.groups) {
            for (const role of group.roles) {
                let location = bron[index++];
                role.location = { x: center + location.x, y: center + location.y, dx: 0, dy: 0 };
            }
            group.initShown(this.map.length);
            for (let x = center - homeHalfSize; x <= center + homeHalfSize; x++) {
                for (let y = center - homeHalfSize; y <= center + homeHalfSize; y++) {
                    group.openShown(x, y);
                }
            }
            group.openShown(center - homeHalfSize - 1, center);
            group.openShown(center + homeHalfSize + 1, center);
            group.openShown(center, center - homeHalfSize - 1);
            group.openShown(center, center + homeHalfSize + 1);
            group.refreshShown();
        }
    }

    step() {
        this.tick++;
        if (this.status === gameConfig.status.counting) {
            if (this.tick === gameConfig.tick.count * gameConfig.tick.fps) {
                console.log('倒计时结束');
                this.status = gameConfig.status.gaming;
            }
        } else if (this.status === gameConfig.status.gaming) {
            for (const group of this.groups) {
                for (const role of group.roles) {
                    let direction = role.step(this);
                    if (this.rescuers.some(rescuer => role.location.x === rescuer.x && role.location.y === rescuer.y)) {
                        role.toOver(this);
                    } else if (direction || group.needCheck) {
                        group.checkShown(this, direction, role);
                    }
                }
            }
            if (this.groups.every(group => group.roles.every(role => role.isOver()))) {
                this.game.over();
            } else {
                this.game.refresh();
            }
        }
    }

    start() {
        console.log('开始倒计时');
        this.status = gameConfig.status.counting;
        this.timer = setInterval(() => this.step(), 1000 / gameConfig.tick.fps);
    }

    over() {
        clearInterval(this.timer);
    }


    createMap(size) {
        const fullSize = size * 4 + home.length
        const homeStart = size * 2;
        const homeEnd = size * 2 + home.length - 1;
        const homeCenter = (homeStart + homeEnd) / 2;
        //const map = new Array(fullSize).fill(0).map((_, y) => new Array(fullSize).fill(0).map((_, x) => y % 2 === 0 && x % 2 === 0 || y === 0 || x === 0 || y === last || x === last ? 1 : 0));
        const map = new Array(fullSize).fill(0).map((_, y) => new Array(fullSize).fill(1));
        
        this.printMap(map, fullSize);

        for (let x = homeStart, hx = 0; x <= homeEnd; x++, hx++) {
            for (let y = homeStart, hy = 0; y <= homeEnd; y++, hy++) {
                map[x][y] = home[hx][hy];
            }
        }

        this.printMap(map, fullSize);

        const exit = exits[Math.floor(Math.random() * exits.length)];
        const x1 = exit.x1 < 0 ? fullSize + exit.x1 : exit.x1;
        const y1 = exit.y1 < 0 ? fullSize + exit.y1 : exit.y1;
        const x2 = exit.x2 < 0 ? fullSize + exit.x2 : exit.x2;
        const y2 = exit.y2 < 0 ? fullSize + exit.y2 : exit.y2;
        map[y1][x1] = map[y2][x2] = 0;
        const start = {x: x2, y: y2};
        const road = [start];
        const entries = [
            {x: homeCenter, y: homeStart - 1},
            {x: homeCenter, y: homeEnd + 1},
            {x: homeStart - 1, y: homeCenter},
            {x: homeEnd + 1, y: homeCenter},
        ];

        let sw = false;
        do {
            if (this.createRoad(map, road, { fullSize, homeStart, homeEnd, entries })) {
                if (!sw) {
                    // this.printMap(map, fullSize);
                }
                sw = true;
            } else {
                sw = false;
            }
        } while(road.length > 1);
        this.printMap(map, fullSize);

        this.rescuers.push({ ...start, dx: 0, dy: 0 });
        this.map = map;
    }

    createRoad(map, road, { fullSize, homeStart, homeEnd, entries }) {
        const latest = road[road.length - 1];
        const nexts = directions.filter(n => {
            const x = latest.x + n.x * 2;
            const y = latest.y + n.y * 2;
            return  (y > 0 && y < fullSize && x > 0 && x < fullSize) && 
                    (y < homeStart || y > homeEnd || x < homeStart || x > homeEnd) && 
                    map[y][x] === 1
        });
        if (nexts.length) {
            const next = nexts[Math.floor(Math.random() * nexts.length)];
            map[latest.y + next.y][latest.x + next.x] = 0;
            map[latest.y + next.y * 2][latest.x + next.x * 2] = 0;
            const current = {x: latest.x + next.x * 2, y: latest.y + next.y * 2};
            road.push(current);

            if (entries.some(entry => entry.x === current.x && entry.y === current.y)) {
                for (const entry of entries) {
                    if (entry.x !== current.x || entry.y !== current.y) {
                        map[entry.y][entry.x] = 0;
                        road.push(entry);
                    }
                }
                this.printMap(map, fullSize);
            }

            return true;
        } else {
            const start = Math.floor(road.length / 2);
            road.pop();
            road.push(road[start]);
            road.splice(start, 1);
            return false;
        }
    }

    printMap(map, size) {
        let mapStr = '';
        for (let y = 0; y < size; y++) {
            for (let x = 0; x < size; x++) {
                mapStr += map[y][x] ? '[]' : '  ';
            }
            mapStr += '\r\n';
        }
        console.log(mapStr);
    }

    get data() {
        return extend(super.data, {
            map: this.map,
            status: this.status,
            tick: this.tick,
            rescuers: this.rescuers.map(rescuer => ({...rescuer})),
            victims: this.game.room.roles.map(role => ({ 
                overTick: role.overTick, 
                location: { ...role.location }, 
                face: role.face,
                group: role.group,
                playerId: role.player.id, 
            }))
        })
    }
}

module.exports = MazeBoard;